/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

/* 
 * File:   fltkplayer.cpp
 * Author: Administrator
 * 
 * Created on 2021年3月5日, 下午3:11
 */

#include "fltkplayer.h"
#include <time.h>

static void _reset_callback(Fl_Widget *w, void *data) {
	Fl_File_Input*p=(Fl_File_Input*)data;
	p->value("");	
}
static void _saveimg_callback(Fl_Widget *w, void *data) {
	fltkplayer*p=(fltkplayer*)data;
	p->saveimg_callback();	
}
static int _sdl_play_thread (void *data){
	fltkplayer*p=(fltkplayer*)data;
	p->play_thread();
	return 0;
}
static unsigned int _sdl_play_timer (unsigned int interval,void *data){
	fltkplayer*p=(fltkplayer*)data;
	p->play_timer();
	return interval;
}
static void _play_callback(Fl_Widget *w, void *data) {	
	SDL_CreateThread(_sdl_play_thread,"SDLPlayer",data);
}
static void _sdl_play_audio_callback(void *userdata, Uint8 *stream, int len){
    fltkplayer*p=(fltkplayer*)userdata;
	p->playaudiobuff(stream,len);
}
static void _pause_callback(Fl_Widget *w, void *data) {
	fltkplayer*p=(fltkplayer*)data;
	p->pause_callback();	
}

static void _stop_callback(Fl_Widget *w, void *data) {
	fltkplayer*p=(fltkplayer*)data;
	p->stop_callback();	
}

fltkplayer::fltkplayer()
{
}

fltkplayer::fltkplayer(const fltkplayer& orig)
{
}

fltkplayer::~fltkplayer()
{
	uninit();
}
/*
Fl_RGB_Image* prepare_shape(int w)
{
  // draw a white circle with a hole in it on black background
  Fl_Image_Surface *surf = new Fl_Image_Surface(w, w);
  Fl_Surface_Device* current = Fl_Surface_Device::surface();
  surf->set_current();
  fl_color(FL_BLACK);
  fl_rectf(-1, -1, w+2, w+2);
  fl_color(FL_WHITE);
  fl_pie(2,2,w-4,w-4,0,360);
  fl_color(FL_BLACK);
  fl_pie(0.7*w,w/2,w/4,w/4,0,360);
  Fl_RGB_Image* img = surf->image();
  delete surf;
  current->set_current();
  return img; // return white image on black background
}*/
void fltkplayer::init()
{
	//m_window = new Fl_Window(800,600,"播放器");
	m_window = new Fl_Double_Window(800,600,"播放器");
	//Fl_RGB_Image *img = prepare_shape(200);
	//m_window->shape(img);
	
	m_input = new Fl_File_Input(40, 10, 480, 40, "File");
	//m_input->callback(_inputfile_callback,(void*)this);
		
	m_resetbutton = new Fl_Button(530, 10, 80, 40, "Reset");
	m_resetbutton->type(FL_NORMAL_BUTTON);
	m_resetbutton->callback(_reset_callback,(void*)m_input);
	
	m_saveimgbutton = new Fl_Button(710, 10, 80, 40, "SaveIMG");
	m_saveimgbutton->type(FL_NORMAL_BUTTON);
	m_saveimgbutton->callback(_saveimg_callback,(void*)this);
	
	//m_playprogress=new Fl_Hor_Value_Slider(40,60,400,40,"00:00:00/00:00:00");
	//m_playprogress->minimum(0);
	//m_playprogress->maximum(0);
	//视频播放按钮
	m_playbutton = new Fl_Button(530, 60, 80, 40, "Play");
	m_playbutton->type(FL_NORMAL_BUTTON);
	m_playbutton->callback(_play_callback,(void*)this);
	
	m_pausebutton = new Fl_Button(620, 60, 80, 40, "Pause");
	m_pausebutton->type(FL_NORMAL_BUTTON);
	m_pausebutton->callback(_pause_callback,(void*)this);
	
	m_stopbutton = new Fl_Button(710, 60, 80, 40, "Stop");
	m_stopbutton->type(FL_NORMAL_BUTTON);
	m_stopbutton->callback(_stop_callback,(void*)this);
	
	//跟下方SDL窗口重叠，不播放时使用此窗口
	m_imgbox=new Fl_Box(FL_FLAT_BOX,40,110,720,480,NULL);
		
	m_window->end();
	//感觉不加没事
	m_window->show(0, NULL);
	printf("fl_graphics_driver:%p\n",fl_graphics_driver);
#if USE_SDL_WIN_PLAY
	//要完成后下面才有HWND
	//用Windows方式创建子控件
	//编译64位的要用GWLP_HINSTANCE，看定义值跟GWL_HINSTANCE一样，在64位中被GWL_HINSTANCE undef
	//貌似返回值一直是0x00400000,实际试验发现HINSTANCE用NULL也行
	HWND tmp_hwnd=CreateWindow (TEXT("STATIC"),"",       
        WS_CHILD | WS_VISIBLE ,40,110,720,480 ,(HWND)fl_xid(m_window), (HMENU) 0,
#if 0		
#ifdef _WIN64
		(HINSTANCE) GetWindowLongPtr ((HWND)fl_xid(m_window), GWLP_HINSTANCE), NULL) ;
#else		
		(HINSTANCE) GetWindowLong ((HWND)fl_xid(m_window), GWL_HINSTANCE), NULL) ;
#endif		
#else		
		NULL,NULL) ;
#endif	
	printf("tmp_hwnd:%p  fltk_hwnd:%p\n",tmp_hwnd,fl_xid(m_window));
	//HWND
	m_sdlwindow=SDL_CreateWindowFrom((void*)tmp_hwnd);
	SDL_HideWindow(m_sdlwindow);
    m_sdlscreen=SDL_GetWindowSurface(m_sdlwindow);
#else
	m_sdlimage=NULL;
#endif	
}
void fltkplayer::uninit(){
	stop_callback();
#if USE_SDL_WIN_PLAY	
	if(m_sdlwindow){
		SDL_DestroyWindow(m_sdlwindow);
		m_sdlwindow=NULL;
		m_sdlscreen=NULL;
	}
#endif	
}

void fltkplayer::saveimg_callback(){
	char filename[1024]={0};
	snprintf(filename,1024,"%ld.jpg",time(0));
#if USE_SDL_WIN_PLAY	
	IMG_SaveJPG(m_sdlscreen,filename,100);
#else
	if(m_sdlimage){
		IMG_SaveJPG(m_sdlimage,filename,100);
	}
#if 0	
//还未找到fltk自带的保存截图	
	Fl_Image* flimg=m_imgbox->image();
	//int R=(3*W+3)/4 * 4;
	//int buffsize=flimg->count();
	//char*buff=(char*)malloc(buffsize);
	//memcpy(buff,(void*)flimg->data(),buffsize);
	if(flimg){
		SDL_Surface*image=SDL_CreateRGBSurfaceWithFormatFrom((void*)flimg->data(),720,480,
				24,720*24/8,SDL_PIXELFORMAT_RGB24);
		IMG_SaveJPG(image,filename,100);
	}
	//free(buff);
#endif	
#endif
}
void fltkplayer::play_thread(){	
	char errmsg[1024];
	FFMS_ErrorInfo ErrorInfo;
	ErrorInfo.Buffer      = errmsg;
	ErrorInfo.BufferSize  = sizeof(errmsg);
	ErrorInfo.ErrorType   = FFMS_ERROR_SUCCESS;
	ErrorInfo.SubType     = FFMS_ERROR_SUCCESS;
	
	FFMS_SetLogLevel(FFMS_LOG_INFO);
	
	FFMS_Indexer *Indexer=FFMS_CreateIndexer(m_input->value(), &ErrorInfo);
	int traces=FFMS_GetNumTracksI(Indexer);
	int i=0;
	while(i<traces){
		printf("ID:%d Type:%d Code:%s\n",i,FFMS_GetTrackTypeI(Indexer,i),FFMS_GetCodecNameI(Indexer,i));
		i++;
	}
	FFMS_TrackTypeIndexSettings(Indexer,FFMS_TYPE_AUDIO,1,0);
	FFMS_Index *Index = FFMS_DoIndexing2(Indexer, FFMS_IEH_ABORT, &ErrorInfo);

	int videoTrace = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_VIDEO, &ErrorInfo);
	int audioTrace = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_AUDIO, &ErrorInfo);

	printf("Fine videoTrace:%d audioTrace:%d traces:%d\n",videoTrace,audioTrace,traces);
	if(videoTrace<0){
		return ;
	}
	
	m_videosource = FFMS_CreateVideoSource(m_input->value(), videoTrace, Index, 1, FFMS_SEEK_NORMAL, &ErrorInfo);
	m_audiosource = FFMS_CreateAudioSource(m_input->value(), audioTrace, Index, FFMS_DELAY_FIRST_VIDEO_TRACK, &ErrorInfo);

	printf("videosource:%p audiosource:%p %s\n",m_videosource,m_audiosource,ErrorInfo.Buffer);

	if(m_videosource){
		const FFMS_VideoProperties *videoprops = FFMS_GetVideoProperties(m_videosource);
		m_videoframes = videoprops->NumFrames;
		m_videopos=0;
		printf("Fps:%d/%d Rff:%d/%d NumFrames:%d FirstTime:%f LastTime:%f\n",
			videoprops->FPSNumerator,videoprops->FPSDenominator,
			videoprops->RFFNumerator,videoprops->RFFDenominator,
			videoprops->NumFrames,videoprops->FirstTime,videoprops->LastTime);
		//printf("Left:%d Right:%d Top:%d Bottom:%d\n",videoprops->CropLeft,videoprops->CropRight,
		//	videoprops->CropTop,videoprops->CropBottom);
		m_FPSDenominator=videoprops->FPSNumerator;
		m_FPSNumerator=videoprops->FPSDenominator;
		m_totaltime=videoprops->LastTime;
		//m_playprogress->bounds(0,m_totaltime);
		//m_playprogress->maximum(m_totaltime);
		//设置输出格式及大小
		do{
			int pixfmts[2];
			//pixfmts[0] = FFMS_GetPixFmt("rgba");
			pixfmts[0] = FFMS_GetPixFmt("rgb24");
			pixfmts[1] = -1;
			int dtsw=m_imgbox->w();
			int dtsh=m_imgbox->h();
			//从第一帧获取宽带和高度
			const FFMS_Frame *propframe = FFMS_GetFrame(m_videosource, 0, &ErrorInfo);
			fltk_count_autosize(propframe->EncodedWidth,propframe->EncodedHeight,&dtsw,&dtsh);
			printf("Real:%dx%d Box:%dx%d Dts:%dx%d\n",propframe->EncodedWidth,propframe->EncodedHeight,
				m_imgbox->w(),m_imgbox->h(),dtsw,dtsh);
			FFMS_SetOutputFormatV2(m_videosource, pixfmts, dtsw, dtsh, FFMS_RESIZER_BICUBIC, &ErrorInfo);
			//m_videowidth=dtsw;
			//m_videoheigh=dtsh;
		}while(0);
			
		//时间是有偏差的
		m_timerid=SDL_AddTimer(1000*videoprops->FPSDenominator/videoprops->FPSNumerator,
			_sdl_play_timer,(void*)this);
		printf("m_timerid:%d\n",m_timerid);
#if USE_SDL_WIN_PLAY		
		SDL_ShowWindow(m_sdlwindow);
#endif		
	}

	if(m_audiosource){
		/*
		FFMS_ResampleOptions* options=FFMS_CreateResampleOptions(m_audiosource);
		options->SampleFormat=FFMS_FMT_S16;
		options->ChannelLayout=2;
		options->SampleRate=44100;
		FFMS_SetOutputFormatA(m_audiosource,options,&ErrorInfo);
		*/
		const FFMS_AudioProperties *audioprops = FFMS_GetAudioProperties(m_audiosource);
		printf("SampleFormat:%d SampleRate:%d BitsPerSample:%d Channels:%d NumSamples:%lld FirstTime:%f LastTime:%f\n",
			audioprops->SampleFormat,audioprops->SampleRate,audioprops->BitsPerSample,audioprops->Channels,
			audioprops->NumSamples,audioprops->FirstTime,audioprops->LastTime);
		m_audiosamles=audioprops->NumSamples;
		m_audiosamlepos=0;
		m_audiobuffsize=1024*audioprops->BitsPerSample*audioprops->Channels/8;
		m_audiobuff=(char*)malloc(m_audiobuffsize);
	
		SDL_AudioSpec want={0},have={0};
		want.callback=_sdl_play_audio_callback;
		want.freq=audioprops->SampleRate;
		want.samples=1024;
		want.channels=audioprops->Channels;
		if(audioprops->SampleFormat==FFMS_FMT_U8){
			want.format=AUDIO_U8;
		}else
		if(audioprops->SampleFormat==FFMS_FMT_S16){
			want.format=AUDIO_S16;
		}else
		if(audioprops->SampleFormat==FFMS_FMT_S32){
			want.format=AUDIO_S32;
		}else
		if(audioprops->SampleFormat==FFMS_FMT_FLT){
			want.format=AUDIO_F32;
		}else
		if(audioprops->SampleFormat==FFMS_FMT_DBL){
			want.format=AUDIO_F32;
		}
		//want.freq=44100;
		//want.samples=1024;
		//want.channels=2;
		//want.format=AUDIO_F32;
		want.userdata=this;
		m_audiodev=SDL_OpenAudioDevice(NULL, 0, &want, &have, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
		SDL_PauseAudioDevice(m_audiodev,0);
	}
}
void fltkplayer::play_timer(){
	char errmsg[1024];
	FFMS_ErrorInfo ErrorInfo;
	ErrorInfo.Buffer      = errmsg;
	ErrorInfo.BufferSize  = sizeof(errmsg);
	ErrorInfo.ErrorType   = FFMS_ERROR_SUCCESS;
	ErrorInfo.SubType     = FFMS_ERROR_SUCCESS;
	const FFMS_Frame *propframe = FFMS_GetFrame(m_videosource, m_videopos, &ErrorInfo);
	if(propframe==NULL){
		stop_callback();
		return;
	}
	if(!m_window->shown()){
		return;
	}
#if USE_SDL_WIN_PLAY	
	if(m_sdlwindow){
		SDL_Surface*image=SDL_CreateRGBSurfaceWithFormatFrom(const_cast<uint8_t*>(propframe->Data[0]),720,480,
				24,propframe->Linesize[0],SDL_PIXELFORMAT_RGB24);
		SDL_BlitSurface(image, NULL, m_sdlscreen, NULL); // blit it to the screen
		SDL_FreeSurface(image);
		// this works just like SDL_Flip() in SDL 1.2
		SDL_UpdateWindowSurface(m_sdlwindow);
	}
#else
//必须设置当前win的hdc
	m_window->make_current();
	int posx=m_imgbox->x()+(m_imgbox->w()-propframe->ScaledWidth)/2;
	int posy=m_imgbox->y();
	fl_draw_image((uchar*)propframe->Data[0],posx,posy,propframe->ScaledWidth,propframe->ScaledHeight,3);
	if(m_sdlimage){
		SDL_FreeSurface(m_sdlimage);
		m_sdlimage=NULL;
	}
	m_sdlimage=SDL_CreateRGBSurfaceWithFormatFrom(const_cast<uint8_t*>(propframe->Data[0]),
		propframe->ScaledWidth,propframe->ScaledHeight,
		24,propframe->Linesize[0],SDL_PIXELFORMAT_RGB24);
	
	float playtime=1.0*m_videopos*m_FPSNumerator/m_FPSDenominator;
	//m_playprogress->value(playtime);
	char showstr[32]={0};
	fltk_showtime_format(playtime,m_totaltime,showstr,32);
	//m_playprogress->label(showstr);	
	//m_playprogress->scrollvalue(playtime,m_totaltime,1,m_totaltime);
	
	//m_playprogress->slider_size(playtime);
	//fl_frame2(showstr,40,60,400,40);
	//fl_draw(showstr,40,70);
	//fl_draw(showstr,m_playprogress->x(),m_playprogress->y());
	//fl_frame(showstr,m_playprogress->x(),m_playprogress->y(),m_playprogress->w(),m_playprogress->h());
	//m_playprogress->label(showstr);
#if 0	
	static int oldplaytime=0;
	if(oldplaytime!=playtime){
		char showstr[32]={0};
		fltk_showtime_format(playtime,m_totaltime,showstr,32);
		m_showtimebutton->label(showstr);
		m_showtimebutton->redraw_label();
		oldplaytime=playtime;
	}
#endif	
//会卡顿	
	//Fl_RGB_Image*tmp=new Fl_RGB_Image((uchar*)propframe->Data[0],720,480,3);
	//m_imgbox->image(tmp);
	//m_imgbox->redraw();
	//m_window->flush();
#endif	
	m_videopos++;
}
void fltkplayer::pause_callback(){
	if(m_videosource==NULL||m_audiosource==NULL){
		return;
	}	
	if(m_videosource){
		if(m_timerid){
			SDL_RemoveTimer(m_timerid);
			m_timerid=0;
#if USE_SDL_WIN_PLAY			
			SDL_HideWindow(m_sdlwindow);
			//画面使用m_imgbox填充
			SDL_Surface*surface=SDL_ConvertSurfaceFormat(m_sdlscreen,SDL_PIXELFORMAT_RGB24,0);			
			Fl_RGB_Image*tmp=new Fl_RGB_Image((uchar*)surface->pixels,surface->w,surface->h,
				surface->format->BitsPerPixel/8,surface->pitch);
			//Fl_Bitmap *tmp=new Fl_Bitmap((uchar*)surface->pixels,surface->w,surface->h);
			Fl_Image* pImg = tmp->copy(720,480);
			m_imgbox->image(pImg);
			m_imgbox->redraw();
			SDL_FreeSurface(surface);
#endif			
		}else{
			const FFMS_VideoProperties *videoprops = FFMS_GetVideoProperties(m_videosource);
			m_timerid=SDL_AddTimer(1000*videoprops->FPSDenominator/videoprops->FPSNumerator,
				_sdl_play_timer,(void*)this);
#if USE_SDL_WIN_PLAY			
			SDL_ShowWindow(m_sdlwindow);
#endif			
		}
	}
	if(m_audiosource){
		if(SDL_GetAudioDeviceStatus(m_audiodev)==SDL_AUDIO_PLAYING){
			SDL_PauseAudioDevice(m_audiodev,1);
		}else{
			SDL_PauseAudioDevice(m_audiodev,0);
		}
	}	
}
void fltkplayer::stop_callback(){
#if USE_SDL_WIN_PLAY	
	SDL_HideWindow(m_sdlwindow);
#endif	
	if(m_timerid){
		SDL_RemoveTimer(m_timerid);
		m_timerid=0;
	}
	if(m_audiodev>0){
		SDL_PauseAudioDevice(m_audiodev,1);
		SDL_CloseAudioDevice(m_audiodev);
		m_audiodev=0;
	}
	if(m_audiobuff){
		free(m_audiobuff);
		m_audiobuffsize=0;
		m_audiobuff=NULL;
	}
	if(m_videosource){
		FFMS_DestroyVideoSource(m_videosource);
		m_videosource=NULL;
	}
	if(m_audiosource){
		FFMS_DestroyAudioSource(m_audiosource);
		m_audiosource=NULL;
	}
}

void fltkplayer::playaudiobuff(void *stream,int len){
	if(m_audiosource){
		char errmsg[1024];
		FFMS_ErrorInfo ErrorInfo;
		ErrorInfo.Buffer      = errmsg;
		ErrorInfo.BufferSize  = sizeof(errmsg);
		ErrorInfo.ErrorType   = FFMS_ERROR_SUCCESS;
		ErrorInfo.SubType     = FFMS_ERROR_SUCCESS;
		FFMS_GetAudio(m_audiosource,m_audiobuff,m_audiosamlepos,1024,&ErrorInfo);
		//SDL_MixAudio((Uint8 *)stream,( const Uint8 * )m_audiobuff,1024*8,SDL_MIX_MAXVOLUME);
		//SDL_QueueAudio(m_audiodev,m_audiobuff,1024*8);
		memcpy(stream,m_audiobuff,m_audiobuffsize);
		m_audiosamlepos+=1024;
		//printf("%d %s %s\n",len,ErrorInfo.Buffer,SDL_GetError());
	}
}




